﻿using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.IdentityModel.Tokens;
using Singer.Core;
using Singer.Shared;

namespace Singer.Middleware.JwtAuth;

/// <summary>
/// Jwt服务拓展
/// </summary>
public static class JwtAuthServiceExtensions
{
    public static void AddJwtAuth(this IServiceCollection services, IConfiguration configuration)
    {
        var config = configuration.GetSection(JwtOptions.ConfigKey);
        JwtOptions? options = config.Get<JwtOptions>();
        if (options == null)
            throw new Exception("【JwtAuth】Config was not found.");
        options.Validate();
        services.Configure<JwtOptions>(config);
        services.AddJwtAuthentication(configuration);//Jwt鉴权
        services.TryAddSingleton<IJwtAuthTokenManager, JwtAuthTokenManager>();//Jwt Token Provider
        services.AddMvc(opt =>
        {
            opt.Filters.Add<JwtAuthCheckFilter>();
        });
    }

    /// <summary>
    /// jwt 鉴权
    /// </summary>
    private static void AddJwtAuthentication(this IServiceCollection services, IConfiguration configuration)
    {
        JwtOptions? options = configuration.GetSection(JwtOptions.ConfigKey).Get<JwtOptions>();
        if (options == null)
            throw new Exception("【JwtAuth】Config was not found.");
        options.Validate();
        SecurityKey securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(options.SecurityKey));
        services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
            .AddJwtBearer(opt =>
            {
                //jwt验证参数设置
                opt.TokenValidationParameters = new TokenValidationParameters
                {
                    //JWT有一些默认的属性，就是给鉴权时就可以筛选了
                    ValidateIssuer = true,//是否验证Issuer--如果要验证---必须让token中的Issuer和这里配置的一样
                    ValidateAudience = true,//是否验证Audience--如果要验证---必须让token中的Audience和这里配置的一样
                    ValidateLifetime = true,//是否验证失效时间
                    ValidateIssuerSigningKey = true,//是否验证SecurityKey
                    ValidAudience = options.Audience,
                    ValidIssuer = options.Issuer,//Issuer，这两项和前面签发jwt的设置一致
                    IssuerSigningKey = securityKey,
                };
                opt.SaveToken = true;//每次请求时都保存token到HttpContext中
                //jwt验证不通过，触发的事件
                opt.Events = new JwtBearerEvents
                {
                    //此处为权限验证失败后触发的事件
                    OnChallenge = async context =>
                    {
                        //此处代码为终止.Net Core默认的返回类型和数据结果，这个很重要哦，必须
                        context.HandleResponse();
                        //自定义返回的数据类型
                        context.Response.ContentType = "application/json";
                        //自定义返回状态码，默认为401 我这里改成 200
                        context.Response.StatusCode = StatusCodes.Status200OK;
                        await context.Response.WriteAsync(JsonUtils.ToJson(new CoreApiResult("auth-error", "身份认证失效")));
                    }
                };
            });
    }
}
